﻿using NetDh.Lib;
using NetDh.Presentation.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;

namespace NetDh.WebApiAndMvc.Sample.Controllers
{
    /// <summary>
    /// 测试TestApiController
    /// </summary>
    //[AllowAnonymous]
    [ApiActionFilter(AllowAnonymous = true, TmpTest = "TestApiController")] //测试时，不带token匿名访问
    public class TestApiController : BaseApiController
    {
        /// <summary>
        /// 测试TestObj
        /// </summary>
        /// <returns>返回测试TestJsonResult实例</returns>
        [Route("test_obj")]//url可以自由设定，不以api开头。
        [HttpPost]
        [HttpGet]
        public TestJsonResult TestObj()
        {
            //默认就是Newtonsoft.Json序列化
            return new TestJsonResult() { UserName = "TestObj" };
        }

        /// <summary>
        /// 查看临时日志
        /// </summary>
        /// <param name="logType">日志类型</param>
        /// <returns>日志列表</returns>
        [Route("getlog/{logType}")]
        [HttpPost]
        [HttpGet]
        public IEnumerable<MemoryLog> GetLog(string logType)
        {
            return MemoryLog.GetLogs(logType);
        }
        [Route("api/TestStringName/{id}")]
        [HttpGet]
        public string TestStringName(string id, string name)
        {
            return "TestStringName:" + name;
        }
        /// <summary>
        /// 测试FromBody参数
        /// </summary>
        /// <param name="id">参数id</param>
        /// <param name="name">参数name</param>
        /// <returns></returns>
        [Route("api/TestFromBodyName/{id}")]
        [HttpPost]
        public string TestFromBodyName(string id, [FromBody]dynamic name)
        {
            //参数是[FromBody]string标记，前端ajax的data需要用JSON.stringify('name02')
            //参数是[FromBody]dynamic name，前端用JSON.stringify({UserId:'id01',UserName:'name01'})，其中dynamic可以改成任何类
            return "TestFromBodyName:" + name;
        }

        [Route("api/TestEx")]
        [HttpPost]
        [HttpGet]
        public string TestException()
        {
            throw new Exception("TestException");
        }

        [Route("api/TestRedirectPost/{id}")]
        [HttpPost]
        public IHttpActionResult TestRedirectPost(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return Redirect("api/testRedirectPost2");
            }
            return Json("TestRedirectPost:" + id);
        }
        [Route("api/TestRedirectPost2/{id}")]
        [HttpPost]
        public IHttpActionResult TestRedirectPost2(string id)
        {
            return Json("TestRedirectPost2:" + id);
        }

        [Route("api/getstring")]
        public string GetString()
        {
            return "get string";
        }

        #region 测试并发异步下取this.CurrentUser和System.Web.HttpContext.Current.Request->前者都正常，后者在异步线程中为null
        [ApiActionFilter(AllowAnonymous = false)]
        [Route("api/TestCurrentUser")]
        [HttpPost]
        public string TestCurrentUser([FromBody]dynamic data)
        {
            var userId = this.CurrentUser.UserId;//-->同一线程中正常
            string paramId = data.id;
            var paramUserId = "uid-" + paramId;
            var threadId = Thread.CurrentThread.ManagedThreadId;
            if (paramUserId != userId)
                return $"error----:【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
            return $"【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
        }
        [ApiActionFilter(AllowAnonymous = false)]
        [Route("api/TestCurrentUser2")]
        [HttpPost]
        public string TestCurrentUser2()
        {
            var userId = this.CurrentUser.UserId;
            var paramId = System.Web.HttpContext.Current.Request["id"];//-->同一线程中正常
            var paramUserId = "uid-" + paramId;
            var threadId = Thread.CurrentThread.ManagedThreadId;
            if (paramUserId != userId)
                return $"error----:【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
            return $"【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
        }
        [ApiActionFilter(AllowAnonymous = false, TmpTest = "TestCurrentUserAsync")]
        [Route("api/TestCurrentUserAsync")]
        [HttpPost]
        public async Task<string> TestCurrentUserAsync([FromBody]dynamic data)
        {
            var mainThreadId = "main-threadId:" + Thread.CurrentThread.ManagedThreadId;
            var result = await Task.Run(() =>
            {
                var userId = this.CurrentUser.UserId;//-->异步线程中正常
                string paramId = data.id;
                var paramUserId = "uid-" + paramId;
                var threadId = Thread.CurrentThread.ManagedThreadId;
                if (paramUserId != userId)
                    return $"error----:【id:{paramId},usertoken.userId:{userId},{mainThreadId},action-threadId:{threadId}】";
                return $"【id:{paramId},usertoken.userId:{userId},{mainThreadId}, action-threadId:{threadId}】";
            });
            return result + ",return-threadId:" + Thread.CurrentThread.ManagedThreadId;
        }
        [ApiActionFilter(AllowAnonymous = false)]
        [Route("api/TestCurrentUserAsync2")]
        [HttpPost]
        public async Task<string> TestCurrentUserAsync2()
        {
            //var paramId = System.Web.HttpContext.Current.Request["id"];//如果放在父线程，则是正常。
            var result = await Task.Run(() =>
            {
                var paramId = System.Web.HttpContext.Current.Request["id"];//-->异步线程中报错，因System.Web.HttpContext.Current为null
                //var paramId = this.HttpWrapper.Request["id"];//如果用我封装的this.HttpWrapper就正常了，支持异步线程。
                var userId = this.CurrentUser.UserId;
                var paramUserId = "uid-" + paramId;
                var threadId = Thread.CurrentThread.ManagedThreadId;
                if (paramUserId != userId)
                    return $"error----:【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
                return $"【id:{paramId},usertoken.userId:{userId},action-threadId:{threadId}】";
            });
            return result;
        }
        #endregion

        #region 测试await不当用法的死锁
        private static async Task<string> GetJsonAsync()
        {
            var str = await Task.Run(() => {
                Thread.Sleep(1000);
                return "task run string return..";
            });
            return str;
        }
        [HttpGet]
        [Route("api/TestDeadLockOnAwait")]
        public string TestDeadLockOnAwait()
        {
            var jsonTask = GetJsonAsync();
            return jsonTask.Result.ToString();//不当用法，导致死锁，执行到这边会卡住。
            /*死锁不是因为await/async，而是因为ui context(winform项目)和request context(web项目)，
             * 这两个上下文一次只能一个线程访问。
             * 如果换成控制台项目，这样写法就不会死锁。*/
        }
        #endregion

        #region async/await/exception

        //异步函数-模拟任务
        private async Task<string> TaskDelayFunc(int id)
        {
            var info = $"beforeAwait:{Thread.CurrentThread.ManagedThreadId}. ";//这句还是第一线程执行
            await Task.Delay(2000);//有真正意义的await Task，才触发await的异步执行，即在第二线程中执行。
            MemoryLog.AddInfon($"id:{id} TaskDelayFunc doing.thread id:{Thread.CurrentThread.ManagedThreadId}");
            return info + $"afterAwait:{Thread.CurrentThread.ManagedThreadId}. ";
        }
        //同步函数-模拟任务
        private async Task<string> TaskDelayFuncWithoutAwait(int id)
        {
            var info = $"beforeAwait:{Thread.CurrentThread.ManagedThreadId}. ";
            Thread.Sleep(2000);//模拟执行了2秒的任务。注意，这边没有await，其实是同步执行。
            MemoryLog.AddInfon($"id:{id} TaskDelayFunc doing.thread id:{Thread.CurrentThread.ManagedThreadId}");
            return info + $"afterAwait:{Thread.CurrentThread.ManagedThreadId}. ";
        }
        [HttpPost]
        [Route("api/TestAsyncPost/{id}")]
        public async Task<string> TestAsyncPost(int id)
        {
            /*如果涉及大量磁盘i/o或网络开销的操作时，则用异步action。如果只是cpu操作时，则用普通同步action*/
            var info = $"id:{id} 主线程是:{Thread.CurrentThread.ManagedThreadId}. ";//进入Action的第一线程执行
            MemoryLog.AddInfon($"id:{id} begin.thread id:{Thread.CurrentThread.ManagedThreadId}");
            var result = await TaskDelayFunc(id);
            //var result = await TaskDelayFuncWithoutAwait(id);//-->TaskDelayFuncWithoutAwait内部无await，是同步执行
            MemoryLog.AddInfon($"id:{id} end.thread id:{Thread.CurrentThread.ManagedThreadId}");
            info = info + result + $"返回线程是:{Thread.CurrentThread.ManagedThreadId}. ";
            return info;
        }
        [HttpPost]
        [Route("api/TestAsyncPostException/{id}")]
        public async Task<string> TestAsyncPostException(int id)
        {
            var info = $" id:{id} 主线程是:{Thread.CurrentThread.ManagedThreadId}. ";//进入Action的第一线程执行
            try
            {
                /* 如果没有await，那么只会catch当前线程的异常，不会catch到Task.Run异步线程里的异常；
                 * 如果有await，那么可以同时catch到当前线程和await异步线程的异常。 */
                await Task.Run(() =>
                {
                    Thread.Sleep(2000);
                    MemoryLog.AddInfon($"id:{id} 异常线程是:{Thread.CurrentThread.ManagedThreadId}");
                    throw new Exception("exception in task");
                });
                MemoryLog.AddInfon($"id:{id} end.thread id:{Thread.CurrentThread.ManagedThreadId}");
                info = info + $"返回线程是:{Thread.CurrentThread.ManagedThreadId}. ";
                return info;
            }
            catch (Exception e)
            {
                return e.Message + info;
            }
        }
        #endregion
    }
}